package io.xmu.dataanalysis.data.analysis;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Sets;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import io.xmu.dataanalysis.data.BaseDataCache;
import io.xmu.dataanalysis.data.DataService;
import io.xmu.dataanalysis.data.OrderClient;
import io.xmu.dataanalysis.data.OrderStatus;
import io.xmu.dataanalysis.data.PageType;
import io.xmu.dataanalysis.entity.Goods;
import io.xmu.dataanalysis.entity.Order;
import io.xmu.dataanalysis.entity.PV;
import io.xmu.dataanalysis.entity.ShopDailyData;
import io.xmu.dataanalysis.entity.ShoppingCart;
import io.xmu.dataanalysis.repository.PVRepo;
import io.xmu.dataanalysis.repository.ShopDailyDataRepo;
import io.xmu.dataanalysis.util.DateUtils;

/**
 * @author lepdou 2017-03-06
 */
@Component
public class ShopDataAnalysis {

  @Autowired
  private PVRepo pvRepo;
  @Autowired
  private ShopDailyDataRepo shopDailyDataRepo;
  @Autowired
  private BaseDataCache dataCache;
  @Autowired
  private DataService dataService;


  @Transactional
  public void analysis(String partition) {
    shopDailyDataRepo.deleteByPartition(partition);

    analysisOrder(partition);
    analysisPV(partition);
    analysisShoppingCart(partition);

  }

  private void analysisOrder(String partition) {
    Iterable<Order> orders = dataService.getOrderByPartition(partition);
    Multimap<Integer, Order> shopOrders = ArrayListMultimap.create();

    Map<Integer, Goods> orderMapGoods = Maps.newHashMap();
    for (Order order : orders) {
      Goods goods = dataCache.getGoods(order.getGoodsId());
      if (goods == null) {
        continue;
      }
      int shopId = goods.getShopId();
      orderMapGoods.put(order.getId(), goods);
      shopOrders.put(shopId, order);
    }

    List<ShopDailyData> shopDailyDatas = Lists.newLinkedList();

    Set<Integer> shopIds = shopOrders.keySet();
    for (Integer shopId : shopIds) {
      Collection<Order> shopDailyOrders = shopOrders.get(shopId);

      long sales = 0;
      int payedGoodsCount = 0;
      int createOrderCount = 0;
      Set<Integer> payedUsers = Sets.newHashSet();

      for (Order order : shopDailyOrders) {

        //sales
        if (OrderStatus.isSuccessOrder(order.getStatus())) {
          sales += orderMapGoods.get(order.getId()).getPrice();
        }

        //payed goods count
        if (order.getStatus() == OrderStatus.PAY.value()) {
          payedGoodsCount++;
          payedUsers.add(order.getUserId());
        }

        //下单订单数
        if (OrderStatus.isSuccessOrder(order.getStatus())) {
          createOrderCount ++;
        }
      }

      ShopDailyData shopDailyData = getShopDailyData(shopId, partition);

      shopDailyData.setSales(sales);
      shopDailyData.setPayedGoodsCount(payedGoodsCount);
      shopDailyData.setPayedUserCount(payedUsers.size());
      shopDailyData.setCreateOrderCount(createOrderCount);
      shopDailyDatas.add(shopDailyData);
    }

      shopDailyDataRepo.save(shopDailyDatas);

  }

  private void analysisPV(String partition) {
    Date startDate = DateUtils.getDateBeginTime(partition);
    Date endDate = DateUtils.getDateEndTime(partition);

    List<PV> pvs = pvRepo.findByCreateTimeAfterAndCreateTimeBefore(startDate, endDate);

    Multimap<Integer, PV> shopIdMapPV = ArrayListMultimap.create();
    for (PV pv : pvs) {
      shopIdMapPV.put(pv.getShopId(), pv);
    }

    List<ShopDailyData> shopDailyDatas = Lists.newLinkedList();

    Set<Integer> shopIds = shopIdMapPV.keySet();
    for (Integer shopId : shopIds) {
      Collection<PV> shopPVs = shopIdMapPV.get(shopId);

      int pvByAppCount = 0;
      int pvByWebCount = 0;
      Set<Integer> userByApp = Sets.newHashSet();
      Set<Integer> userByWeb = Sets.newHashSet();
      Set<Integer> allUsers = Sets.newHashSet();
      long stayTime = 0;

      Multiset<Integer> counter = HashMultiset.create();
      Multiset<Integer> goodsCounter = HashMultiset.create();
      for (PV pv : shopPVs) {
        stayTime += pv.getViewTime();
        int userId = pv.getUserId();
        allUsers.add(userId);
        counter.add(userId);
        if (pv.getClient() == OrderClient.MOBILE.value()) {
          pvByAppCount++;
          userByApp.add(pv.getUserId());
        } else {
          pvByWebCount++;
          userByWeb.add(pv.getUserId());
        }

        if (pv.getType() == PageType.GOODS.value()) {
          goodsCounter.add(pv.getGoodsId());
        }
      }

      int visitOnePageUsers = 0;
      for (Integer userId: counter.elementSet()) {
        if (counter.count(userId) == 1) {
          visitOnePageUsers ++;
        }
      }

      ShopDailyData shopDailyData = getShopDailyData(shopId, partition);

      int pvCount = pvByAppCount + pvByWebCount;
      shopDailyData.setPv(pvCount);
      shopDailyData.setViewGoodsCount(goodsCounter.size());
      shopDailyData.setVisitOnlyOnePageUsers(visitOnePageUsers);
      shopDailyData.setPvByApp(pvByAppCount);
      shopDailyData.setUv(allUsers.size());
      shopDailyData.setUvByApp(userByApp.size());
      shopDailyData.setAvgStayTime((int) (stayTime / pvCount));

      shopDailyDatas.add(shopDailyData);
    }

    shopDailyDataRepo.save(shopDailyDatas);
  }

  private void analysisShoppingCart(String partition) {
    Iterable<ShoppingCart> carts = dataService.getShoppingByPartition(partition);
    Multimap<Integer, ShoppingCart> shopMapCart = ArrayListMultimap.create();

    for (ShoppingCart cart: carts) {
      shopMapCart.put(dataCache.getGoods(cart.getGoodsId()).getShopId(), cart);
    }

    List<ShopDailyData> shopDailyDatas = Lists.newLinkedList();

    Set<Integer> shopIds = shopMapCart.keySet();
    for (Integer shopId : shopIds) {
      ShopDailyData shopDailyData = getShopDailyData(shopId, partition);
      shopDailyData.setAddShoppingCartCount(shopMapCart.get(shopId).size());
      shopDailyDatas.add(shopDailyData);
    }

    shopDailyDataRepo.save(shopDailyDatas);
  }

  private ShopDailyData getShopDailyData(int shopId, String partition) {
    ShopDailyData shopDailyData = shopDailyDataRepo.findByShopIdAndPartition(shopId, partition);
    if (shopDailyData == null) {
      shopDailyData = new ShopDailyData();
      shopDailyData.setShopId(shopId);
      shopDailyData.setCateId(dataCache.getShop(shopId).getCateId());
      shopDailyData.setPartition(partition);
    }
    return shopDailyData;
  }
}
